iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0

「一句喊出口的指控會隨風散去;一份寫下來、分好段落的指控,才經得起明天全村逐字翻查。」
——《阿帕契開源審計錄》¹ 卷三·提案篇

幕間
敘事者在心中盤算決戰:「神職只剩女巫了。只要我白天自爆控刀,二號夜裡刀掉女巫,狼人屠邊獲勝!」
他對這條必勝路徑深信不疑:前幾世,六號也這樣自稱獵人,他從沒親眼看過這個人開槍。他完全沒發覺自己正依循一張被神明竄改過的地圖邁向毀滅,更沒想到那個座位上,只是一個演得太好的好人。

宣讀前的那一夜,七號沒有站起來指著二號大喊「他是狼」。她把羊皮紙攤在膝上,用炭筆在頁面上畫了四條橫線,分成四個帶標題的區塊,然後一格一格往裡填。隔壁的平民探頭問她:明天開口講不就好了,寫這些做什麼?她頭也沒抬:「講出來的話,這一輪結束就沒人記得細節了;寫下來的東西,明天他們可以一行一行挑我毛病。我要的就是這個。」

長桌對面,二號的手指在桌沿下方輕輕比劃,像是在跟著某個節奏記數。有人問他在做什麼,他說「隨手比劃」,然後把手收了回去。他從來沒有讓任何人看過他到底記了什麼。

那一夜法官罕見地離了一會兒席。他起身走動時,我第一次看清:面具底下是有輪廓的,長袍裡是有重量的——他不是一道憑空的聲音,是一個人。是人,也許就有能被攔下、被說動的一天。這個念頭讓我心跳漏了一拍,我沒敢往下想。

一份要被全村挑剔的指控,為什麼值得先花一夜寫成文件?因為開源世界裡最重要的溝通,本來就長這個樣子。

提 PR 的本業是寫小作文

新手以為一個 Pull Request 就是一段 diff:程式碼貼上去,等人按合併。做過幾年的人知道,diff 是最不花力氣的部分,真正的工作是那段說明。維護者一天可能收到幾十個 PR,他憑什麼先看你的?憑你有沒有把四件事講清楚:Context(背景)、Problem(痛點)、Solution(設計取捨)、Verification(測試證明)。多數被晾在一旁沒人理的 PR,不是程式碼爛,而是作者只丟了程式碼、什麼都沒解釋,等於要審查者替他把上下文重建一遍。

維護者的時間是專案裡最稀缺的資源。一份好的 PR 說明,等於幫他把「這個改動要不要收」這個決策的前置作業做完:他不需要去翻你引用的那張 issue、不需要 checkout 你的分支才知道你在解什麼、不需要猜你為什麼選 A 不選 B。你替他省下的每一分鐘,都會回報在合併速度上。反過來,一份逼審查者當考古學家的 PR,通常的下場就是被標記「等作者補充」,然後石沉大海。這四段不是官僚表格,是把你腦中的決策過程外部化,讓別人能接手判斷。

  • Context:這個改動屬於哪個子系統、哪個版本、哪組設定,關聯的 issue 是哪一張。審查者要能靠這段話,在腦中把你面對的情境重建出來——沒有 Context,後面三段他根本無從判斷。
  • Problem:一個具體的失敗,最好附上重現步驟或一個會紅的測試。只講這一個痛,不要順手夾帶三個不相關的修補。
  • Solution:你選的設計,兩三句話講完;然後老實交代你放棄了哪些替代方案、接受了什麼取捨;再標出「影響半徑」——這個 PR 明確不動到什麼。
  • Verification:新增了哪些測試、它們斷言什麼、審查者要怎麼在本地重跑出同樣的綠燈。行為或效能有變,就附上 benchmark 或 log。
## Context
- Subsystem, version, config flags in play. Link the issue.
- What a reviewer must know to evaluate this change at all.

## Problem
- One concrete failure: reproduction steps or a failing test.
- Why it matters now, and who is affected.

## Solution
- The design chosen, in two or three sentences.
- Alternatives rejected, and the trade-off accepted.
- Blast radius: what this change explicitly does NOT touch.

## Verification
- New tests and what they assert.
- How to reproduce the green result locally.
- Benchmarks or logs if behaviour or performance shifted.

壞的 Context 常常只是把標題再講一次:標題寫「修好登入逾時」,Context 就寫「這個 PR 修好了登入逾時的問題」——等於沒寫。有資訊量的 Context 會補上標題塞不下的東西:哪個模組、從哪個版本開始出現、觸發條件是什麼、可以用哪個指令重現、關聯的 issue 是哪一張。判準很簡單:一個從沒碰過這塊程式碼的人讀完,能不能大致知道你站在哪裡。

讓審查者在同意你之前,先有機會反對你

四段裡最容易寫壞的是 Problem 和 Solution,因為作者太清楚答案,會把問題寫成結論。

Problem 要能脫離你的解法單獨成立。 審查者必須能在還沒看你怎麼修之前,就自己判斷「這確實是問題、值得現在處理」。如果 Problem 段寫成「因為 X 函式沒加鎖,所以我加了鎖」,他就沒有機會反對你的問題認定,只能連著解法一起吞。正確的寫法是先講現象:什麼輸入、什麼併發條件、觀察到什麼錯誤結果,讓他先點頭「對,這要修」,再往下看。

Solution 的價值在你捨棄的那些選項。 只說「我用了方案 A」等於什麼都沒說;說「我考慮過在呼叫端加檢查,但那會讓每個使用者都得記得做一次,所以改成在建構子強制」,審查者才知道你想過、也知道可以從哪裡挑戰你。被你寫下來否決掉的方案,幫他省下了「這人有沒有想到這個」的來回。

Verification 要指名道姓。 「測試通過」不是證明,因為舊測試本來就通過。可信的 Verification 會說:這個具體案例在改動前會失敗、改動後會通過。最好的形式就是一個新測試——它同時是 Problem 的重現和 Solution 的驗收:

func TestParseTimeline_RejectsBrokenInput(t *testing.T) {
	// Before this PR: decode() swallowed the error and returned an empty
	// Accusation, so a malformed line passed silently as "no evidence".
	_, err := ParseTimeline("night=3;actor=") // actor value is missing

	if err == nil {
		t.Fatal("want a parse error for malformed input, got nil")
	}
}

一次一件事:PR 的標題與體積

四段結構之外,還有兩個常被忽略的細節。第一是標題:它是這份 PR 的一行摘要,用祈使句寫「做了什麼」,而不是「我改了一些東西」——維護者在一長串列表裡靠標題決定點不點進來。第二是體積:一個 PR 只做一件邏輯上完整的事。

一個同時做兩件事的 PR,代價是實打實的。審查者得同時在腦中維護兩條 Context,注意力被稀釋,漏看的機率上升;Verification 寫不乾淨,因為兩件事的驗收糾纏在一起;真的出事要回滾時,你被迫連好的那半也一起退掉;日後有人用 git bisect 追一個 regression,定位到這個 commit 卻分不清是哪一半造成的。順手改的排版、擦到的 typo、想到就加的小功能,全部拆成獨立的 PR。七號的指控之所以有效,正因為她只指控一件事——二號的固定模式——而不是把二號三十天所有可疑的地方全倒出來。

審查者的心智模型

一份好的 PR 說明,是照著審查者腦子裡的檢查順序寫的。他讀 Context 是為了重建你的世界;讀 Problem 是為了確認這個痛是真的、而且只有一個;讀 Solution 是為了檢查你的取捨站不站得住;讀 Verification 是為了自己動手重跑一次。任何一關過不了,他就按下 Request Changes,而且通常不會告訴你他卡在哪一段——所以四段都要先自己走過一遍。

https://ithelp.ithome.com.tw/upload/images/20260924/20183684MgDex1ELrB.png

在維護者之前,先讓機器讀一遍

四段結構寫得再工整,作者終究是當事人——自己最容易對自己的邏輯漏洞視而不見。這幾年開始普及的做法,是在 PR 送到人類審查者手上之前,先讓一個自動化的審查關卡跑過一輪。以 Claude Code 為例,它內建的程式碼審查能力可以針對一份 diff 或指定的 PR,在你設定的嚴謹程度下抓正確性錯誤與可簡化之處,並且能直接把發現的問題以行內留言的形式貼回 PR,或是在確認後直接把修正套用回工作樹——等於是把「審查者的心智模型」那張流程圖,提前跑了一遍空機。Anthropic 也把這個能力做成官方的 GitHub Actions 整合,讓它在 PR 一開啟時就自動介入,而不必每次手動觸發。

這不是要取代七號那份手寫的指控書,而是把它的第一輪挑剔外包出去:邏輯漏洞、明顯的正確性問題,讓機器先挑掉;真正值得維護者花時間的架構取捨與業務判斷,才留給人。

回到牌桌:可以被審查的指控

同一個夜裡,狼的那張桌子也在寫自己的提案,只是內容短得多。我先開口提了一個目標,二號沒有多問,點了一下頭。刀就這樣定了,誰也沒有留下一個字的紀錄。

七號寫完的時候天快亮了。第二天她不是用喊的,而是一段一段唸出來:先交代她從第幾夜開始記、記了哪些欄位(Context);再指出二號那條「從不第一個開口」的固定模式(Problem);接著說明她為什麼排除「他只是性格保守」這個解釋(Solution);最後攤開羊皮紙上那張逐夜對照表(Verification)。

全場第一次沒有跟她的語氣吵架,而是直接跳進她的對照表裡找漏洞——這反而代表他們終於把她的話當一回事了。一份寫得夠結構化的指控,換來的不是掌聲,是被人認真拆解的資格。你的 PR 也一樣:目標從來不是一次過關,而是讓討論能聚焦在真正的技術點上,而不是卡在「你到底想幹嘛」。至於被逐句挑剔會發生什麼事、要怎麼接住那些 -1,那是明天的功課。

讀完這篇,你現在該做的是:下一個 PR,先把 Context、Problem、Solution、Verification 四段寫完,再動手改程式碼;打開 Kubernetes 或 Kafka 的貢獻指南,照它的 checklist 對一遍自己的說明。Google 工程實踐的 Code Review 指南與 opensource.guide 都有完整的 PR 寫作教學可以照著練。

參考資料與延伸閱讀


¹ 註:本書名為情境設定之虛構文獻,非真實歷史或開源紀錄。


上一篇
Day 26|狼的偶發破綻,就是一個 flaky test
下一篇
Day 28|指控書被逐句挑剔:把 Request Changes 當成免費的架構會診
系列文
狼人自爆的心路歷程:一個「AI人」的30天自學修煉 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言